package uk.gov.swmprobation.cplite;

import java.net.URISyntaxException;

import org.fusesource.hawtbuf.Buffer;
import org.fusesource.hawtbuf.UTF8Buffer;
import org.fusesource.mqtt.client.Callback;
import org.fusesource.mqtt.client.CallbackConnection;
import org.fusesource.mqtt.client.Listener;
import org.fusesource.mqtt.client.MQTT;
import org.fusesource.mqtt.client.QoS;
import org.fusesource.mqtt.client.Topic;

import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.IBinder;
import android.provider.Settings;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.TaskStackBuilder;
import android.util.Log;

public class MQTTConnection extends Service {

	final private String PACKAGE_NAME = "uk.gov.swmprobation.cpvisibility";
	final private String MAKE_CONNECTION = PACKAGE_NAME + ".MAKECONNECTION";
	final private String END_CONNECTION = PACKAGE_NAME + ".ENDCONNECTION";
	final private String SUBSCRIBE_TOPIC = PACKAGE_NAME + ".SUBSCRIBETOPIC";
	final private String PUBLISH_MESSAGE = PACKAGE_NAME + ".PUBLISHMESSAGE";
	final private String UNSUBSCRIBE_TOPIC = PACKAGE_NAME + ".UNSUBSCRIBETOPIC";
	final private String RESUBSCRIBE_ALL = PACKAGE_NAME + ".RESUBSCRIBEALL";
	//final private String sAddress = "tcp://test.mosquitto.org:1883";
	//final private String sAddress = "tcp://cpvisibility.org:1883";
	final private String sAddress = "tcp://37.61.197.153:1883";
	final private short KEEP_ALIVE = 60 * 30;
	
	MQTT mqtt = null;
	
	CallbackConnection connection = null;
	
	BroadcastReceiver backgroundUpdate;
	
	public void onCreate(){
		 backgroundUpdate = new BroadcastReceiver(){

			@Override
			public void onReceive(Context contrext, Intent intent) {
				if (intent.getAction().equals(SUBSCRIBE_TOPIC)){
					String topic = intent.getStringExtra("Topic");
					subscribeTopic(topic);
				} else if (intent.getAction().equals(PUBLISH_MESSAGE)){
					String topic = intent.getStringExtra("Topic");
					String message = intent.getStringExtra("Message");
					publishMessage(topic,message);
				} else if (intent.getAction().equals(UNSUBSCRIBE_TOPIC)){
					String topic = intent.getStringExtra("Topic");
					unsubscribeTopic(topic);
				}else if (intent.getAction().equals(MAKE_CONNECTION)){
					makeConnection();
				} else if (intent.getAction().equals(END_CONNECTION)){	
					endConnection();
				} else if (intent.getAction().equals(RESUBSCRIBE_ALL)){	
					resubscribeAll();
				}
			}
	    	
	    }; 
	    
	    IntentFilter intentFilter = new IntentFilter();
	    intentFilter.addAction(MAKE_CONNECTION);
	    intentFilter.addAction(END_CONNECTION);
	    intentFilter.addAction(SUBSCRIBE_TOPIC);
	    intentFilter.addAction(UNSUBSCRIBE_TOPIC);
	    intentFilter.addAction(PUBLISH_MESSAGE);
	    intentFilter.addAction(RESUBSCRIBE_ALL);
	    intentFilter.setPriority(1);
	    registerReceiver(backgroundUpdate, intentFilter);
	    
	    mqtt = new MQTT();
		mqtt.setClientId(Settings.Secure.getString(getContentResolver(),Settings.Secure.ANDROID_ID));
		mqtt.setKeepAlive(KEEP_ALIVE);
		mqtt.setCleanSession(DatabaseHandler.getProjectsCount()==DatabaseHandler.getProjectsCountType(Project.TYPE_DRAFT));
		try
		{
			mqtt.setHost(sAddress);
 			Log.d("MQTT", "Address set: " + sAddress);
		}
		catch(URISyntaxException urise)
		{
			//Log.e("MQTT", "URISyntaxException connecting to " + sAddress + " - " + urise);
		}
		

		connection = mqtt.callbackConnection();
		connection.listener(new Listener() {

		    public void onDisconnected() {
		    	//Log.e("MQTT","Disconnected");
		    }
		    public void onConnected() {
		    	//Log.e("MQTT","Connected");
		    }
		    
			@Override
			public void onFailure(Throwable arg0) {
				// TODO Auto-generated method stub
				StackTraceElement[] stackTrace = arg0.getStackTrace();
				//Log.e("MQTT",arg0.toString());
				//for (int i=0; i<stackTrace.length; i++) Log.e("MQTT", Integer.toString(i) + ":" + stackTrace[i]);
			}
			@Override
			public void onPublish(UTF8Buffer arg0, Buffer arg1, Runnable arg2) {
				String subject = String.valueOf(arg0);
				String message = String.valueOf(arg1.ascii());
				subject = subject.split("/")[1];
				if(message != null){
					DatabaseHandler.flagUpdated(Integer.valueOf(subject), message);
					createNotification(subject, message);
					Intent broadcastIntent = new Intent("uk.gov.swmprobation.cpvisibility.event.updatemain");
					sendOrderedBroadcast(broadcastIntent, null);
					arg2.run();
				}
			}
		});

		makeConnection();
		
	}
	
	public void onDestroy(){
		//Log.e("MQTT","Destroyed");
		unregisterReceiver(backgroundUpdate);
	}
	
	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		return null;
	}
	
	private void makeConnection(){
		//Log.e("MQTT","Start making connection");
		/*
		if (isNetworkConnected() && !checkConnection())
		{
		*/		
		connection.connect(new Callback<Void>() {
		    public void onFailure(Throwable value) {
		    	//Log.e("MQTT","Failed Here: "+ value); // If we could not connect to the server.
		    }

		    // Once we connect..
		    public void onSuccess(Void v) {
		    	//Log.e("MQTT","Success Here");
		    }
		    
		});
	}
	
	private void subscribeTopic(final String topic){
		// Subscribe to a topic
        Topic[] topics = {new Topic(topic, QoS.EXACTLY_ONCE)};
        connection.subscribe(topics, new Callback<byte[]>() {
            public void onSuccess(byte[] qoses) {
            	//Log.e("MQTT","Subscribed:"+topic);
            }
            public void onFailure(Throwable value) {
            	//Log.e("MQTT","Failed Subscribe:" + value + " - Topic:" + topic); // subscribe failed.
            }
        });
	}
	
	private void unsubscribeTopic(final String topic){
		UTF8Buffer[] topicBuffer = {UTF8Buffer.utf8(topic)};
		connection.unsubscribe(topicBuffer, new Callback<Void>() {
            public void onFailure(Throwable value) {
            	//Log.e("MQTT","Failed Unsubscribe:" + value); // subscribe failed.
            }
			@Override
			public void onSuccess(Void v) {
				//Log.e("MQTT","Unsubscribed:"+topic);				
			}
        });
	}
	
	private void publishMessage(String topic, String message){
		//Log.e("MQTT","Publish message");
		// Send a message to a topic
        connection.publish(topic, message.getBytes(), QoS.EXACTLY_ONCE, false, new Callback<Void>() {
            public void onSuccess(Void v) {
            	//Log.e("MQTT","Published");
            }
            public void onFailure(Throwable value) {
            	//Log.e("MQTT","Failed Publish:" + value); // publish failed.
            }
        });
	}
	
	private void endConnection(){
		//Log.e("MQTT","Ending Connection");
			connection.disconnect(new Callback<Void>(){

				@Override
				public void onFailure(Throwable value) {
					//Log.e("MQTT","Disconnected on Request Failed: "+value);					
				}

				@Override
				public void onSuccess(Void v) {
					//Log.e("MQTT","Disconnected on Request");					
				}
				
			});
	}

	private void resubscribeAll(){
		cleanSession();
	}
	
	private void cleanSession(){
		connection.disconnect(new Callback<Void>(){

			@Override
			public void onFailure(Throwable value) {
				//Log.e("MQTT","Disconnected on Request Failed 1: "+value);					
			}

			@Override
			public void onSuccess(Void v) {
				mqtt.setCleanSession(true);	
				connection = mqtt.callbackConnection();
				connection.disconnect(new Callback<Void>(){

					@Override
					public void onFailure(Throwable value) {
						//Log.e("MQTT","Disconnected on Request Failed 1: "+value);						
					}

					@Override
					public void onSuccess(Void arg0) {
						mqtt.setCleanSession(false);
						connection = mqtt.callbackConnection();
					}
					
				});
			}
			
		});
	}
	
	
	private void createNotification(String projectid,String status){
		String contentText;
		String contentTitle;
		
		if(status.equals("new")){
			contentText = Helper.shortName(DatabaseHandler.projectName(projectid))+" has been submitted.";
			contentTitle = "Project Submitted";
		} else {
			contentText = Helper.shortName(DatabaseHandler.projectName(projectid))+" - "+status+" has been updated.";
			contentTitle = "Project Updated";
		}
		
		int intProjectid = Integer.valueOf(projectid);
		NotificationCompat.Builder mBuilder =
				new NotificationCompat.Builder(this)
				.setAutoCancel(true)
				.setSmallIcon(R.drawable.ic_launcher)	
				.setContentText(contentText)
				.setContentTitle(contentTitle);
				//Creates an explicit intent for an Activity in your app
				Intent resultIntent = new Intent(this, ReviewProjects.class);
				resultIntent.putExtra("project_id", intProjectid);
				resultIntent.putExtra("updated", true);

				//The stack builder object will contain an artificial back stack for the
				//started Activity.
				//This ensures that navigating backward from the Activity leads out of
				//your application to the Home screen.
				TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
				//Adds the back stack for the Intent (but not the Intent itself)
				stackBuilder.addParentStack(MainActivity.class);
				//Adds the Intent that starts the Activity to the top of the stack
				stackBuilder.addNextIntent(resultIntent);
				PendingIntent resultPendingIntent =
				stackBuilder.getPendingIntent(
				    0,
				    PendingIntent.FLAG_UPDATE_CURRENT
				);
				mBuilder.setContentIntent(resultPendingIntent);
				NotificationManager mNotificationManager =
				(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
				//mId allows you to update the notification later on.
				mNotificationManager.notify(intProjectid, mBuilder.build());
	}
}
